package com.bumptech.glide.util;

import com.bumptech.glide.load.model.Model;
import com.bumptech.glide.request.target.Target;
import ohos.eventhandler.EventRunner;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.ImageInfo;
import ohos.media.image.common.PixelFormat;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/** A collection of assorted utility classes. */
public final class Util {
  public static final int THREAD_PRIORITY_BACKGROUND =10;
  public static final int THREAD_PRIORITY_MORE_FAVORABLE = -1;
  private static final int HASH_MULTIPLIER = 31;
  private static final int HASH_ACCUMULATOR = 17;
  private static final char[] HEX_CHAR_ARRAY = "0123456789abcdef".toCharArray();
  // 32 bytes from sha-256 -> 64 hex chars.
  private static final char[] SHA_256_CHARS = new char[64];
  public static final int COLOR_TRANS = 0x00FFFFFF;     //TRANS color

  private Util() {
    // Utility class.
  }

  /** Returns the hex string of the given byte array representing a SHA256 hash.
   * @param bytes
   * @return string
   */
  @NotNull
  public static String sha256BytesToHex(@NotNull byte[] bytes) {
    synchronized (SHA_256_CHARS) {
      return bytesToHex(bytes, SHA_256_CHARS);
    }
  }

  // Taken from:
  // http://stackoverflow.com/questions/9655181/convert-from-byte-array-to-hex-string-in-java
  // /9655275#9655275
  @SuppressWarnings("PMD.UseVarargs")
  @NotNull
  private static String bytesToHex(@NotNull byte[] bytes, @NotNull char[] hexChars) {
    int v;
    for (int j = 0; j < bytes.length; j++) {
      v = bytes[j] & 0xFF;
      hexChars[j * 2] = HEX_CHAR_ARRAY[v >>> 4];
      hexChars[j * 2 + 1] = HEX_CHAR_ARRAY[v & 0x0F];
    }
    return new String(hexChars);
  }

 
  /**
   * Returns the allocated byte size of the given pixelmap.
   *
   * @see #getBitmapByteSize(Bitmap)
   * @deprecated Use {@link #getBitmapByteSize(Bitmap)} instead. Scheduled to be
   *     removed in Glide 4.0.
   * @param pixelmap
   * @return int
   */
  @Deprecated
  public static int getSize(@NotNull PixelMap pixelmap) {
    return getBitmapByteSize(pixelmap);
  }

  /** Returns the in memory size of the given {@link Bitmap} in bytes.
   * @param pixelmap
   * @return int
   */
  public static int getBitmapByteSize(@NotNull PixelMap pixelmap) {
    // The return value of getAllocationByteCount silently changes for recycled bitmaps from the
    // internal buffer size to row bytes * height. To avoid random inconsistencies in caches, we
    // instead assert here.
    if (pixelmap.isReleased()) {
      throw new IllegalStateException(
          "Cannot obtain size for recycled pixelmap: "
              + pixelmap);
    }

    // Workaround for KitKat initial release NPE in Bitmap, fixed in MR1. See issue #148.
    try {
        return (int)pixelmap.getPixelBytesCapacity();
    } catch (
        @SuppressWarnings("PMD.AvoidCatchingNPE")
        NullPointerException e) {
        // Do nothing.
    }

    return pixelmap.getImageInfo().size.height * pixelmap.getBytesNumberPerRow();
  }

  /**
   * Returns the in memory size of {@link Bitmap} with the given width, height, and
   * {@link Bitmap.Config}.
   * @param width
   * @param height
   * @param config
   * @return int
   */
  public static int getBitmapByteSize(int width, int height, @Nullable PixelFormat config) {
    return width * height * getBytesPerPixel(config);
  }

  private static int getBytesPerPixel( @Nullable PixelFormat config) {
    // A bitmap by decoding a GIF has null "config" in certain environments.
    if (config == null) {
      config = PixelFormat.ARGB_8888;
    }

    int bytesPerPixel;
    switch (config) {      //Note: Other formats are yet to be supported
      case RGB_565:
        bytesPerPixel = 2;
        break;
      case ARGB_8888:
      default:
        bytesPerPixel = 4;
        break;
    }
    return bytesPerPixel;
  }

  /** Returns true if width and height are both > 0 and/or equal to {@link Target#SIZE_ORIGINAL}.
   * @param width
   * @param height
   * @return boolean
   */
  public static boolean isValidDimensions(int width, int height) {
    return isValidDimension(width) && isValidDimension(height);
  }

  private static boolean isValidDimension(int dimen) {
    return dimen > 0 || dimen == Target.SIZE_ORIGINAL;
  }

  /**
   * Throws an {@link IllegalArgumentException} if called on a thread other than the main
   * thread.
   */
  public static void assertMainThread() {
    if (!isOnMainThread()) {
      throw new IllegalArgumentException("You must call this method on the main thread");
    }
  }

  /**
   * Throws an {@link IllegalArgumentException} if called on the main thread.
   */
  public static void assertBackgroundThread() {
    if (!isOnBackgroundThread()) {
      throw new IllegalArgumentException("You must call this method on a background thread");
    }
  }

  /** Returns {@code true} if called on the main thread, {@code false} otherwise.
   * @return boolean
   */
  public static boolean isOnMainThread() {
    return EventRunner.current() == EventRunner.getMainEventRunner();
  }

  /** Returns {@code true} if called on a background thread, {@code false} otherwise.
   * @return boolean
   */
  public static boolean isOnBackgroundThread() {
    return !isOnMainThread();
  }

  /**
   * Creates a {@link Queue} of the given size using Glide's preferred implementation.
   * @param size
   * @return queue
   */
  @NotNull
  public static <T> Queue<T> createQueue(int size) {
    return new ArrayDeque<>(size);
  }

  /**
   * Returns a copy of the given list that is safe to iterate over and perform actions that may
   * modify the original list.
   *
   * <p>See #303, #375, #322, #2262.
   * @param other
   * @return list of T
   */
  @NotNull
  @SuppressWarnings("UseBulkOperation")
  public static <T> List<T> getSnapshot( @NotNull Collection<T> other) {
    // toArray creates a new ArrayList internally and does not guarantee that the values it contains
    // are non-null. Collections.addAll in ArrayList uses toArray internally and therefore also
    // doesn't guarantee that entries are non-null. WeakHashMap's iterator does avoid returning null
    // and is therefore safe to use. See #322, #2262.
    List<T> result = new ArrayList<>(other.size());
    for (T item : other) {
      if (item != null) {
        result.add(item);
      }
    }
    return result;
  }

  /**
   * Null-safe equivalent of {@code a.equals(b)}.
   *
   * @see java.util.Objects#equals
   * @param a
   * @param b
   * @return boolean
   */
  public static boolean bothNullOrEqual(@Nullable Object a, @Nullable Object b) {
    return a == null ? b == null : a.equals(b);
  }

  public static boolean bothModelsNullEquivalentOrEquals(@Nullable Object a, @Nullable Object b) {
    if (a == null) {
      return b == null;
    }
    if (a instanceof Model) {
      return ((Model) a).isEquivalentTo(b);
    }
    return a.equals(b);
  }

  public static int hashCode(int value) {
    return hashCode(value, HASH_ACCUMULATOR);
  }

  public static int hashCode(int value, int accumulator) {
    return accumulator * HASH_MULTIPLIER + value;
  }

  public static int hashCode(float value) {
    return hashCode(value, HASH_ACCUMULATOR);
  }

  public static int hashCode(float value, int accumulator) {
    return hashCode(Float.floatToIntBits(value), accumulator);
  }

  public static int hashCode( @Nullable Object object, int accumulator) {
    return hashCode(object == null ? 0 : object.hashCode(), accumulator);
  }

  public static int hashCode(boolean value, int accumulator) {
    return hashCode(value ? 1 : 0, accumulator);
  }

  public static int hashCode(boolean value) {
    return hashCode(value, HASH_ACCUMULATOR);
  }


  public static void updateImageInfo(ImageInfo imginfo, ImageSource.DecodingOptions options) {

        options.desiredSize.width = imginfo.size.width;
        options.desiredSize.height = imginfo.size.height;        
    }

    
public static void getImageInfo(InputStream is,  ImageSource.DecodingOptions options) throws IOException
  {
        ImageSource imageSource =null;
        ImageSource.SourceOptions srcOpts = new ImageSource.SourceOptions();

        imageSource = ImageSource.create(is, srcOpts);
        if(null != imageSource)
           LogUtil.info("Util", "imageSource not null");
        else
           LogUtil.info("Util", "imageSource null");

        if (imageSource == null) {
            throw new FileNotFoundException();
        }

        Util.updateImageInfo(imageSource.getImageInfo(), options);
  }

  public static PixelMap decodePixelMap(InputStream is,  ImageSource.DecodingOptions options, String formatHint)throws IOException{
    ImageSource.SourceOptions srcOpts = new ImageSource.SourceOptions();
    srcOpts.formatHint = formatHint;
    ImageSource imageSource =ImageSource.create(is, srcOpts);
    if(null != imageSource)
      LogUtil.info("Util", "imageSource not null");
    else
      LogUtil.info("Util", "imageSource null");

    if (imageSource == null) {
      throw new FileNotFoundException();
    }

    return imageSource.createPixelmap(options);
  }

  public static boolean isEmpty(CharSequence str) {
    if (str == null || str.length() == 0)
      return true;
    else
      return false;
  }
}

